package server

import (
	"errors"
	"github.com/akirakazu/go-simplejson"
	"github.com/gorilla/websocket"
	"log"
	"time"
)

// Message type through send channel
type WriteStruct struct {
	Data []byte
	T    int
}

type wsConn struct {
	// The websocket connection.
	ws *websocket.Conn
	id string
	pb *portBinder
	// Buffered channel of outbound messages
	send chan *WriteStruct
}

func newWsConn(ws *websocket.Conn, pb *portBinder, id string) *wsConn {
	w := new(wsConn)
	w.ws = ws
	w.pb = pb
	w.id = id
	w.ws.SetPingHandler(func(string) error {
		data := []byte("")
		return w.ws.WriteControl(websocket.PongMessage, data, time.Now().Add(10*time.Second))
	})
	w.ws.SetPongHandler(func(string) error {
		w.keepAlive()
		return nil
	})
	w.send = make(chan *WriteStruct, 1000)
	w.keepAlive()
	return w
}

// Read goroutine
func (w *wsConn) readPump() {
	for {
		// Quit this goroutine
		if w.ws == nil {
			return
		}
		messageType, p, err := w.ws.ReadMessage()
		if err != nil {
			log.Println("read error: ", err)
			w.close()
			return
		}

		addr := w.ws.RemoteAddr().String()

		if messageType == websocket.TextMessage {
			buf := string(p)
			_, err := json.NewJson([]byte(buf))
			if err != nil {
			} else {
				m, err := Validate(buf)
				if err != nil {
					w.pb.closeConn(w.id)
					return
				}

				w.pb.server.logPack("recv", m, addr)
				w.pb.server.onTextMessage(w.id, m)
			}
		} else if messageType == websocket.BinaryMessage {
			w.pb.server.logBinary("recv", p, addr)
			w.pb.server.onBinMessage(w.id, p)
		}
	}
}

func (w *wsConn) keepAlive() {
	w.ws.SetReadDeadline(time.Now().Add(150 * time.Second))
	w.ws.SetWriteDeadline(time.Now().Add(150 * time.Second))
}

func (w *wsConn) write(mt int, payload []byte) error {
	if w.ws != nil {
		return w.ws.WriteMessage(mt, payload)
	} else {
		return errors.New("connection already closed")
	}
}

// Write goroutine
func (w *wsConn) writePump() {
	for {
		// Quit this goroutine
		if w.ws == nil {
			return
		}
		message := <-w.send
		err := w.write(message.T, message.Data)
		if err != nil {
			log.Println("write error: ", err)
			w.close()
			return
		}
	}
}

// Close websocket connection
func (w *wsConn) close() {
	if w.ws != nil {
		w.ws.Close()
		w.ws = nil
	}
	if w.pb != nil {
		w.pb.closeConn(w.id)
		w.pb = nil
	}
}
